import { Application, Context, IResponseBodyData } from "egg";

const permissionSymbolKey = Symbol.for("dinegg#permissionAccessControl");

/** 权限控制函数 */
async function checkAccess(permId: string[], methodName: string, ctx: Context, app: Application) {
	// @ts-ignore 该属性为一个类，由外部用户定义，提供控制方法权限的自定义实现类
	if (!app.PermissionAccessControl) {
		// 如果用户未定义权限控制自定义类，则执行到此处表示想要控制却没有定义类，应该报错。
		return {
			isError: true,
			msg: `PermissionAccessControl Error: the app.PermissionAccessControl Class is not provide.`,
		};
	}
	app.coreLogger.debug("方法权限控制：%s %s %s", permId, methodName, ctx.request.path);

	// @ts-ignore
	const ins: PermissionAccessControl = new app.PermissionAccessControl({
		ctx,
		app,
		permId,
		methodName,
	});
	if (!ins.main) {
		return {
			isError: true,
			msg: `PermissionAccessControl Error: the Class must be provide the [main] method and it should return Promise.`,
		};
	}
	try {
		const res = await ins.main();
		if (res == null) return { isError: false, msg: null };
		if (res === false) {
			return { isError: true, msg: `Your don't have permission! ` };
		} else if (typeof res === "string") {
			return { isError: true, msg: `Your don't have permission! ${res} ` };
		}
		return { isError: false, msg: null };
	} catch (error) {
		app.coreLogger.debug(
			"方法权限控制验证权限throw：%s %s %s \r\n %s",
			permId,
			methodName,
			ctx.request.path,
			(error as any).message
		);
		return { isError: true, msg: `Your don't have permission: ${(error as any).message} ` };
	}
}

/**
 * 绑定方法权限标识的装饰器，实现控制器http请求时的权限控制
 * - 需外部定义角色验证规则来配合
 */
export function permission(permId: string | string[]) {
	permId = Array.isArray(permId) ? permId : [permId];

	return function (prototype: Object, property: string, descriptor: PropertyDescriptor) {
		const oldMethod = descriptor.value;
		let metaPermId = Reflect.getMetadata(permissionSymbolKey, prototype, property);
		if (!metaPermId) {
			metaPermId = [];
			Reflect.defineMetadata(permissionSymbolKey, metaPermId, prototype, property);
		}
		metaPermId.push(...permId);

		// 这里this可能是controller的实例，也可能是service的实例
		descriptor.value = async function (this: any, ...args: any[]) {
			const checkResult = await checkAccess(
				Reflect.getMetadata(permissionSymbolKey, prototype, property) || [],
				property,
				this.ctx,
				this.app
			);
			if (checkResult.isError) {
				this.ctx.status = 401; //设置400响应 http

				// 权限校验未通过，按标准设置响应前端json
				return {
					code: 50000,
					msg: checkResult.msg,
					data: null,
					details: [checkResult.msg],
				} as IResponseBodyData;
			}
			return await oldMethod.apply(this, args);
		};

		// descriptor.value[permissionSymbolKey] = oldMethod[permissionSymbolKey];
	};
}

export const Permission = permission;

/** 定义抽象类外部继承实现权限控制自定义类 */
export abstract class AbstractPermissionAccessControl implements PermissionAccessControl {
	app: Application;
	ctx: Context;
	permId: string[];
	methodName: string;

	constructor(opts: { app: Application; ctx: Context; methodName: string; permId: string[] }) {
		this.app = opts.app;
		this.ctx = opts.ctx;
		this.permId = opts.permId;
		this.methodName = opts.methodName;
	}
	/** 权限控制类调用入口方法 */
	abstract main(): Promise<string | boolean>;
}

export interface PermissionAccessControl {
	app: Application;
	ctx: Context;
	permId: string[];
	methodName: string;
	main(): Promise<boolean | string>;
}
